#ifdef WIN32
	#include <windows.h>
#endif

#ifdef MACX_DEV
#include <gl.h>
#else
#include <GL/gl.h>
#endif

#include <stdio.h>
#include <assert.h>

#include "environment.h"

using namespace PBVP;

const unsigned int Environment::MAX_LIGHT_COUNT = 8;

static GLenum LightIDs[8] = {
	GL_LIGHT0, GL_LIGHT1, GL_LIGHT2, GL_LIGHT3, 
	GL_LIGHT4, GL_LIGHT5, GL_LIGHT6, GL_LIGHT7
};

Environment::Environment()
: m_DefaultColor(Color::COLOR_MAGENTA),
m_BackgroundColor(Color::COLOR_BLACK)
{
	m_NumOfLights = 0;
	m_CullMode = NO_CULL;

	// add default lights
	Color ldiff(0.9f, 0.8f, 0.4f, 1.0f);
	Color lspec(0.4f, 0.4f, 0.1f, 1.0f);
	Vector4 lpos0(30.0f, -10.0f, 20.0f, 0.0f);
	Light light0(Color::COLOR_BLACK, ldiff, lspec, lpos0);
	addLight(light0);
	Vector4 lpos1(-30.0f, -10.0f, -20.0f, 0.0f);
	Light light1(Color::COLOR_BLACK, ldiff, lspec, lpos1);
	addLight(light1);
}

Environment::Environment(const Light& lit, const Material& mat)
: m_Material(mat)
{
	m_NumOfLights = 0;
	addLight(lit);
	m_CullMode = NO_CULL;
}

Environment::Environment(const Environment& env)
{
	m_NumOfLights = env.m_NumOfLights;
	m_Material = env.m_Material;
	m_BackgroundColor = env.m_BackgroundColor;
	m_DefaultColor = env.m_DefaultColor;
	m_CullMode = env.m_CullMode;
	for (unsigned int i = 0; i < m_NumOfLights; i++) {
		m_LightList.push_back(env.m_LightList[i]);
	}
	m_ViewTransform = env.m_ViewTransform;
}

Environment::Environment(const QString& fname) 
{
	readConfig(fname);
}

Environment& Environment::operator = (const Environment& env)
{
	if (this == &env) return *this;
	m_NumOfLights = env.m_NumOfLights;
	m_Material = env.m_Material;
	m_BackgroundColor = env.m_BackgroundColor;
	m_DefaultColor = env.m_DefaultColor;
	m_CullMode = env.m_CullMode;
	m_LightList.clear();
	for (unsigned int i = 0; i < m_NumOfLights; i++) {
		m_LightList.push_back(env.m_LightList[i]);
	}
	m_ViewTransform = env.m_ViewTransform;
	return *this;
}

Light& Environment::light(unsigned int n) 
{
	assert (n < m_NumOfLights);
	return m_LightList[n];  
}

const Light& Environment::light(unsigned int n) const 
{
	assert (n < m_NumOfLights);
	return m_LightList[n];  
}

bool Environment::addLight(const Light& lit) 
{
	if (m_NumOfLights == MAX_LIGHT_COUNT) return false;
	m_LightList.push_back(lit);
	m_NumOfLights++;
	return true;
}

void Environment::removeLight(unsigned int n) 
{
	assert(n < m_NumOfLights);
	m_LightList.removeAt(n);
	m_NumOfLights --;
	return;
}

void Environment::apply()
{
	unsigned int i;
	// setup lights
	if (numOfLights() > 0) {
		glEnable(GL_LIGHTING);
	} else {
		glDisable(GL_LIGHTING);
	}
	glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
	// glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);

	for (i = 0; i < MAX_LIGHT_COUNT; i++) glDisable(LightIDs[i]);
	for (i = 0; i < numOfLights(); i++) {
		light(i).apply(LightIDs[i]);
		glEnable(LightIDs[i]);
	}
	m_Material.apply(GL_FRONT);

	// clear framebuffer to background color
	glClearColor(m_BackgroundColor.r, m_BackgroundColor.g, m_BackgroundColor.b, m_BackgroundColor.a); 
	glClearDepth(1.0);
	glColor3f(m_DefaultColor.r, m_DefaultColor.g, m_DefaultColor.b);

	glEnable(GL_DEPTH_TEST);
	if (m_CullMode != NO_CULL) {
		glEnable(GL_CULL_FACE);
		if (m_CullMode == CULL_BACK) glCullFace(GL_BACK);
		else if (m_CullMode == CULL_FRONT) glCullFace(GL_FRONT);
	} else {
		glDisable(GL_CULL_FACE);
	}
	glEnable(GL_NORMALIZE);

	// enable blending
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

bool Environment::readConfig(const QString& fname)
{
	FILE *fp;
	fp = fopen(fname.toStdString().c_str(), "r");
	if (fp == NULL)	return false;

	int n, i;
	float ambt[4], diff[4], spec[4], post[4], shin;
	// read configuration info about light
	fscanf(fp, "%d\n", &n);
	for (i = 0; i < n; i++) {
		fscanf(fp, "%f %f %f %f\n", &ambt[0], &ambt[1], &ambt[2], &ambt[3]);
		fscanf(fp, "%f %f %f %f\n", &diff[0], &diff[1], &diff[2], &diff[3]);
		fscanf(fp, "%f %f %f %f\n", &spec[0], &spec[1], &spec[2], &spec[3]);
		fscanf(fp, "%f %f %f %f\n", &post[0], &post[1], &post[2], &post[3]);
		Color ca(ambt);
		Color cd(diff);
		Color cs(spec);
		Light lit(ca, cd, cs, post);
		addLight(lit);
	}
	// read configuration info about material
	fscanf(fp, "%d\n", &n);
	if (n == 1) {				  // n = 1 means existance of material setting
		fscanf(fp, "%f %f %f %f\n", &ambt[0], &ambt[1], &ambt[2], &ambt[3]);
		fscanf(fp, "%f %f %f %f\n", &diff[0], &diff[1], &diff[2], &diff[3]);
		fscanf(fp, "%f %f %f %f\n", &spec[0], &spec[1], &spec[2], &spec[3]);
		fscanf(fp, "%f \n", &shin);
		Color ca(ambt); Color cd(diff); Color cs(spec);
		Material matl(ca, cd, cs, shin);
		setMaterial(matl);
	}
	fclose(fp);
	return true;
}

